﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Geometries;

namespace YJKGridTopologyCal.Operation.Polygonize
{
	public class Polygonizer
	{
		public bool IsCheckingRingsValid
		{
			get
			{
				return this._isCheckingRingsValid;
			}
			set
			{
				this._isCheckingRingsValid = value;
			}
		}

		public Polygonizer() : this(false)
		{
			this._lineStringAdder = new Polygonizer.LineStringAdder(this);
		}

		public Polygonizer(bool extractOnlyPolygonal)
		{
			this._extractOnlyPolygonal = extractOnlyPolygonal;
			this._lineStringAdder = new Polygonizer.LineStringAdder(this);
		}

		public void Add(ICollection<IGeometry> geomList)
		{
			foreach (IGeometry g in geomList)
			{
				this.Add(g);
			}
		}

		public void Add(IGeometry g)
		{
			g.Apply(this._lineStringAdder);
		}

		private void Add(ILineString line)
		{
			this._geomFactory = line.Factory;
			if (this._graph == null)
			{
				this._graph = new PolygonizeGraph(line.Factory);
			}
			this._graph.AddEdge(line);
		}

		public ICollection<IGeometry> GetPolygons()
		{
			this.Polygonize();
			return this._polyList;
		}

		public IGeometry GetGeometry()
		{
			if (this._geomFactory == null)
			{
				this._geomFactory = new GeometryFactory();
			}
			this.Polygonize();
			if (this._extractOnlyPolygonal)
			{
				return this._geomFactory.BuildGeometry(this._polyList);
			}
			return this._geomFactory.CreateGeometryCollection(GeometryFactory.ToGeometryArray(this._polyList));
		}

		public ICollection<ILineString> GetDangles()
		{
			this.Polygonize();
			return this._dangles;
		}

		public ICollection<ILineString> GetCutEdges()
		{
			this.Polygonize();
			return this._cutEdges;
		}

		public IList<IGeometry> GetInvalidRingLines()
		{
			this.Polygonize();
			return this._invalidRingLines;
		}

		private void Polygonize()
		{
			if (this._polyList != null)
			{
				return;
			}
			this._polyList = new List<IGeometry>();
			if (this._graph == null)
			{
				return;
			}
			this._dangles = this._graph.DeleteDangles();
			this._cutEdges = this._graph.DeleteCutEdges();
			IList<EdgeRing> edgeRings = this._graph.GetEdgeRings();
			List<EdgeRing> list = new List<EdgeRing>();
			this._invalidRingLines = new List<IGeometry>();
			if (this.IsCheckingRingsValid)
			{
				Polygonizer.FindValidRings(edgeRings, list, this._invalidRingLines);
			}
			else
			{
				list = (List<EdgeRing>)edgeRings;
			}
			this.FindShellsAndHoles(list);
			Polygonizer.AssignHolesToShells(this._holeList, this._shellList);
			this._shellList.Sort(new EdgeRing.EnvelopeComparator());
			bool includeAll = true;
			if (this._extractOnlyPolygonal)
			{
				Polygonizer.FindDisjointShells(this._shellList);
				includeAll = false;
			}
			this._polyList = Polygonizer.ExtractPolygons(this._shellList, includeAll);
		}

		private static void FindValidRings(IEnumerable<EdgeRing> edgeRingList, ICollection<EdgeRing> validEdgeRingList, ICollection<IGeometry> invalidRingList)
		{
			foreach (EdgeRing edgeRing in edgeRingList)
			{
				if (edgeRing.IsValid)
				{
					validEdgeRingList.Add(edgeRing);
				}
				else
				{
					invalidRingList.Add(edgeRing.LineString);
				}
			}
		}

		private void FindShellsAndHoles(IEnumerable<EdgeRing> edgeRingList)
		{
			this._holeList = new List<EdgeRing>();
			this._shellList = new List<EdgeRing>();
			foreach (EdgeRing edgeRing in edgeRingList)
			{
				edgeRing.ComputeHole();
				if (edgeRing.IsHole)
				{
					this._holeList.Add(edgeRing);
				}
				else
				{
					this._shellList.Add(edgeRing);
				}
			}
		}

		private static void AssignHolesToShells(IEnumerable<EdgeRing> holeList, List<EdgeRing> shellList)
		{
			foreach (EdgeRing holeEdgeRing in holeList)
			{
				Polygonizer.AssignHoleToShell(holeEdgeRing, shellList);
			}
		}

		private static void AssignHoleToShell(EdgeRing holeEdgeRing, IList<EdgeRing> shellList)
		{
			EdgeRing edgeRing = EdgeRing.FindEdgeRingContaining(holeEdgeRing, shellList);
			if (edgeRing != null)
			{
				edgeRing.AddHole(holeEdgeRing);
			}
		}

		private static void FindDisjointShells(List<EdgeRing> shellList)
		{
			Polygonizer.FindOuterShells(shellList);
			bool flag;
			do
			{
				flag = false;
				foreach (EdgeRing edgeRing in shellList)
				{
					if (!edgeRing.IsIncludedSet)
					{
						edgeRing.UpdateIncluded();
						if (!edgeRing.IsIncludedSet)
						{
							flag = true;
						}
					}
				}
			}
			while (flag);
		}

		private static void FindOuterShells(List<EdgeRing> shellList)
		{
			foreach (EdgeRing edgeRing in shellList)
			{
				EdgeRing outerHole = edgeRing.OuterHole;
				if (outerHole != null && !outerHole.IsProcessed)
				{
					edgeRing.IsIncluded = true;
					outerHole.IsProcessed = true;
				}
			}
		}

		private static List<IGeometry> ExtractPolygons(List<EdgeRing> shellList, bool includeAll)
		{
			List<IGeometry> list = new List<IGeometry>();
			foreach (EdgeRing edgeRing in shellList)
			{
				if (includeAll || edgeRing.IsIncluded)
				{
					list.Add(edgeRing.Polygon);
				}
			}
			return list;
		}

		public const bool AllPolys = false;

		private readonly Polygonizer.LineStringAdder _lineStringAdder;

		private PolygonizeGraph _graph;

		private ICollection<ILineString> _dangles = new List<ILineString>();

		private ICollection<ILineString> _cutEdges = new List<ILineString>();

		private IList<IGeometry> _invalidRingLines = new List<IGeometry>();

		private List<EdgeRing> _holeList;

		private List<EdgeRing> _shellList;

		private ICollection<IGeometry> _polyList;

		private bool _isCheckingRingsValid = true;

		private readonly bool _extractOnlyPolygonal;

		private IGeometryFactory _geomFactory;

		private class LineStringAdder : IGeometryComponentFilter
		{
			public LineStringAdder(Polygonizer container)
			{
				this._container = container;
			}

			public void Filter(IGeometry g)
			{
				ILineString lineString = g as ILineString;
				if (lineString != null)
				{
					this._container.Add(lineString);
				}
			}

			private readonly Polygonizer _container;
		}
	}
}
